
#include <ctime>
#include <iostream> 
#include <algorithm>
#include <thread>
#include <random>
#include <string>
#include <numbers>
#include <format>
#include <string_view>
#include <cstdio>

#include <winerror.h>
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")

using namespace std;

#define OutErr(a) cout << (a) << endl \
      << "出错代码："<< WSAGetLastError() << endl \
      << "出错文件："<< __FILE__ << endl  \
      << "出错行数："<< __LINE__ << endl \


#define PORT 9090
#define DATA_BUFSIZE 8192

/// 结构体定义
typedef struct
{
	OVERLAPPED Overlapped;
	WSABUF DataBuf;
	CHAR Buffer[DATA_BUFSIZE];
}PER_IO_OPERATION_DATA, * LPPER_IO_OPERATION_DATA;


typedef struct
{
	SOCKET Socket;
}PER_HANDLE_DATA, * LPPER_HANDLE_DATA;

///////////////////////////////////////////////////////////////////////
//
// 函数名       : BindServerOverlapped
// 功能描述     : 绑定端口，并返回一个 Overlapped 的ListenSocket
// 参数         : int nPort
// 返回值       : SOCKET
//
///////////////////////////////////////////////////////////////////////
SOCKET BindServerOverlapped(unsigned short nPort);

DWORD WINAPI ProcessIO(LPVOID lpParam);

int main(int argc, char const* argv[])
{
	// 初始化windows网络库
	WSADATA wsd;
	if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
	{
		OutErr("WSAStartup()");
	}
	
	// 创建完成端口（IOCP）
	HANDLE CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);

  
	//线程数目=cpu逻辑核心的两倍.
	for (int i = 0; i < std::thread::hardware_concurrency()*2; i++)
	{
		// 创建工作线程，线程函数传递参数为完成端口（CompletionPort）
		HANDLE hProcessIO =
			CreateThread(NULL, 0, ProcessIO, CompletionPort, 0, NULL);
		if (hProcessIO)
		{
			CloseHandle(hProcessIO);
		}
	}

	//创建侦听SOCKET
	SOCKET sListen = BindServerOverlapped(PORT);

	SOCKET sClient;
	LPPER_HANDLE_DATA PerHandleData;
	LPPER_IO_OPERATION_DATA PerIoData;
	while (true)
	{
		// 等待客户端接入
		//sClient = WSAAccept(sListen, NULL, NULL, NULL, 0);
		sClient = accept(sListen, 0, 0);
		cout << "Socket " << sClient << "连接进来" << endl;

		PerHandleData = new PER_HANDLE_DATA();
		PerHandleData->Socket = sClient;

		// 将接入的客户端和完成端口联系起来
		CreateIoCompletionPort((HANDLE)sClient, CompletionPort, (DWORD)PerHandleData, 0);

		// 建立一个Overlapped，并使用这个Overlapped结构对socket投递操作
		PerIoData = new PER_IO_OPERATION_DATA();

		ZeroMemory(PerIoData, sizeof(PER_IO_OPERATION_DATA));
		PerIoData->DataBuf.buf = PerIoData->Buffer;
		PerIoData->DataBuf.len = DATA_BUFSIZE;

		// 投递一个WSARecv操作
		DWORD Flags = 0;
		DWORD dwRecv = 0;
		WSARecv(sClient, &PerIoData->DataBuf, 1, &dwRecv, &Flags, &PerIoData->Overlapped, NULL);
	}

	DWORD dwByteTrans;
	//将一个已经完成的IO通知添加到IO完成端口的队列中.
	 //提供了与线程池中的所有线程通信的方式.
	PostQueuedCompletionStatus(CompletionPort, dwByteTrans, 0, 0);  //IO操作完成时接收的字节数.

	closesocket(sListen);


	WSACleanup();
	return 0;
}

SOCKET BindServerOverlapped(unsigned short nPort)
{
	// 创建socket
	SOCKET sServer =
		WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
	// SOCKET sServer = socket(AF_INET, SOCK_STREAM, 0);
	// 绑定端口
	struct sockaddr_in servAddr;
	servAddr.sin_family = AF_INET;
	servAddr.sin_port = htons(nPort);
	servAddr.sin_addr.s_addr = htonl(INADDR_ANY);

	if (bind(sServer, (struct sockaddr*)&servAddr, sizeof(servAddr)) < 0)
	{
		OutErr("bind Failed!");
		return NULL;
	}

	// 设置监听队列为SOMAXCONN
	if (listen(sServer, SOMAXCONN) != 0)
	{
		OutErr("listen Failed!");
		return NULL;
	}
	return sServer;
}

DWORD WINAPI ProcessIO(LPVOID lpParam)
{
	HANDLE CompletionPort = (HANDLE)lpParam;
	DWORD BytesTransferred;
	LPPER_HANDLE_DATA PerHandleData;
	LPPER_IO_OPERATION_DATA PerIoData;

	while (true)
	{
		if (0 == GetQueuedCompletionStatus(CompletionPort, &BytesTransferred, (PULONG_PTR)&PerHandleData, (LPOVERLAPPED*)&PerIoData, INFINITE))
		{
			if ((GetLastError() == WAIT_TIMEOUT) || (GetLastError() == ERROR_NETNAME_DELETED))
			{
				cout << "closingsocket" << PerHandleData->Socket << endl;
				closesocket(PerHandleData->Socket);

				delete PerIoData;
				delete PerHandleData;
				continue;
			}
			else
			{
				OutErr("GetQueuedCompletionStatus failed!");
			}
			return 0;
		}

		// 说明客户端已经退出
		if (BytesTransferred == 0)
		{
			cout << "closing socket" << PerHandleData->Socket << endl;
			closesocket(PerHandleData->Socket);
			delete PerIoData;
			delete PerHandleData;
			continue;
		}

		// 取得数据并处理
		cout << PerHandleData->Socket << "发送过来的消息：" << PerIoData->Buffer << endl;

		// 继续向 socket 投递WSARecv操作
		DWORD Flags = 0;
		DWORD dwRecv = 0;
		ZeroMemory(PerIoData, sizeof(PER_IO_OPERATION_DATA));
		PerIoData->DataBuf.buf = PerIoData->Buffer;
		PerIoData->DataBuf.len = DATA_BUFSIZE;
		WSARecv(PerHandleData->Socket, &PerIoData->DataBuf, 1,
			&dwRecv, &Flags, &PerIoData->Overlapped, NULL);
	}

	return 0;
}
